#include "Functions.h"
#include <cstring>
#include <map>
#include <iomanip>

using std::string;
using std::stringstream;
using std::bitset;
using std::hex;
using std::cout;
using std::endl;

namespace convertTypes {

	string intToString(int num) {
		stringstream strm;
		string s_num;
		strm << num;
		strm >> s_num;

		return s_num;
	}

	int hexToInt (char value) {
		int num = 0;
		if (value >= '0' && value <= '9')
			num = value-'0';
		else if (value >= 'A' && value <= 'Z')
			num = value-'A'+10;
		else if (value >= 'a' && value <= 'z')
			num = value-'a'+10;
		return num;
	}

	string toLower (string upper) {
		string lower = "";
		int length = upper.length();
		for (int i=0; i<length; i++) {
			if ((upper[i]>='A') && (upper[i]<='Z'))
				lower += (char)tolower((int)upper[i]);
			else
				lower += upper[i];
		}
		return lower;
	}

	int strToint (std::string str) {
		std::stringstream ss(str);
		int n;
		ss >> n;
		return n;
	}

	bitset<8> strToBitset(std::string str){
		bitset<8> bs;
		bs.reset();
		if (str.size()<=8){
			for (unsigned int i=0; i< str.size(); i++){
				int bit = (int)(str.at(i));
				if(bit == 49){
					bs.set(i,1);
				}else{
					bs.set(i,0);
				}
			}
		}
		return bs;
	}

	bitset<32> strToBitset32(std::string str){
		bitset<32> bs;
		bs.reset();
		if (str.size()<=32){
			for (unsigned int i=0; i< str.size(); i++){
				int bit = (int)(str.at(i));
				if(bit == 49){
					bs.set(31 - i,1);
				}
			}
		}
		return bs;
	}

	bitset<32> hexaToBitset(string hex) {
		bitset<32> bitset;
		bitset.reset();

		return bitset;
	}

	unsigned int hexaToUInt(char* hexa) {
		unsigned int number = 0;
		for (unsigned int i=0; i<4; i++) {
			// separar el caracter en los 2 caracteres hexa, pasarlo a binario concatenarlo y usarlo en la linea siguiente
			uint8_t num = hexa[i];
			if ( hexa[i] < 0) {
				num += 256;
			}
			number += (unsigned int)num * (pow( 16, 6-(i*2)));
		}
		return number;
	}

	string hexaToString (unsigned int num) {
		stringstream sstr(string(""));

		sstr <<	std::setfill ('0') << std::setw(sizeof(int)*2) << hex << num;
		return hexaToBits(sstr.str());
	}

	unsigned int charToInt(char cadena[]) {
		return hexaToUInt(cadena);
	}

	int8_t bitSetToInt(bitset<8> bs) {
		int8_t retorna = 0;
		for (int i=7; i>=0; i--){
			retorna += bs[7-i]*pow(2, i);
		}
		return retorna;
	}

	//Debe recibir solamente un string de 8 caracteres
	uint8_t stringToInt8(string str){
		uint8_t retorna = 0;
		if (str.size()==8){
			for (unsigned int i=0; i< str.size(); i++){
				int bit = (int)(str.at(i));
				if(bit == 49){
					retorna += pow(2, (7-i));
				}
			}
		return retorna;
		}
		return -1;
	}

	std::string hexaToBits(std::string cadenaHexa) {
		std::string s_num = string("");
		for (unsigned int i=0; i<cadenaHexa.length(); i+=2) {
			string bits = getHexCharacter(cadenaHexa.at(i));
			bits.append(getHexCharacter(cadenaHexa.at(i+1)));
			uint8_t integer = stringToInt8(bits);
	//		if(integer == 0)
	//			integer = (uint8_t) '\0';
			s_num.append(1, (char)integer);
		}
		return s_num;
	}

	std::string getHexCharacter(char c) {
		c = toupper(c);
		/*std::map<char, std::string> hexcharmap;
		hexcharmap.insert(std::pair<char, std::string>('F', "1111"));
		hexcharmap.insert(std::pair<char, std::string>('E', "1110"));
		hexcharmap.insert(std::pair<char, std::string>('D', "1101"));
		hexcharmap.insert(std::pair<char, std::string>('C', "1100"));
		hexcharmap.insert(std::pair<char, std::string>('B', "1011"));
		hexcharmap.insert(std::pair<char, std::string>('A', "1010"));
		hexcharmap.insert(std::pair<char, std::string>('9', "1001"));
		hexcharmap.insert(std::pair<char, std::string>('8', "1000"));
		hexcharmap.insert(std::pair<char, std::string>('7', "0111"));
		hexcharmap.insert(std::pair<char, std::string>('6', "0110"));
		hexcharmap.insert(std::pair<char, std::string>('5', "0101"));
		hexcharmap.insert(std::pair<char, std::string>('4', "0100"));
		hexcharmap.insert(std::pair<char, std::string>('3', "0011"));
		hexcharmap.insert(std::pair<char, std::string>('2', "0010"));
		hexcharmap.insert(std::pair<char, std::string>('1', "0001"));
		hexcharmap.insert(std::pair<char, std::string>('0', "0000"));

		std::string result = "";
		result = (hexcharmap.find(c))->second;
	*/
		switch (c) {
			case 'F':
				return string("1111");
			case 'E':
				return string("1110");
			case 'D':
				return string("1101");
			case 'C':
				return string("1100");
			case 'B':
				return string("1011");
			case 'A':
				return string("1010");
			case '9':
				return string("1001");
			case '8':
				return string("1000");
			case '7':
				return string("0111");
			case '6':
				return string("0110");
			case '5':
				return string("0101");
			case '4':
				return string("0100");
			case '3':
				return string("0011");
			case '2':
				return string("0010");
			case '1':
				return string("0001");
			case '0':
				return string("0000");
			default:
				break;
		}
		return "";
	}

	void printQueryToken(QueryToken* qt) {
		cout << "printing" << endl;
		cout << "\t" << qt->word << " " << qt->pos << " documentos: " << endl;
		if(qt->v_data != NULL) {
			for (unsigned int i=0; i<qt->v_data->size(); i++) {
				cout << "\t\t" << "nro doc: "<< qt->v_data->at(i).document << " frec: " << qt->v_data->at(i).frecuency << " ";
				cout << "posiciones: ";
				for(unsigned int j=0; j<qt->v_data->at(i).pos->size(); j++) {
					cout << ((qt->v_data->at(i).pos->at(j))) << " ";
				}
				cout << endl;
			}
		}
	}


	void printQueryTokens(vector<QueryToken>* qts) {
		if (qts != NULL)
			for (unsigned int i=0; i<qts->size(); i++)
				printQueryToken(&(qts->at(i)));
	}


	void printTimeToFindNResults(clock_t initialTime, unsigned int n) {
		clock_t chrono1 = clock();

		string salida = (string(" resultado"));
		if (n > 1)
			salida.append("s");
		salida.append(" en ");
		cout << n << salida << " (" << ((float)chrono1-(float)initialTime)/ CLOCKS_PER_SEC << " segundos)" << endl;

	}
	void printNoResultsFound() {
		cout << "No se encontraron resultados para su búsqueda.\n"
				"Verifique haber ingresado las palabras correctamente." << endl;
	}

	void cleanVectorData(vector<Data>* datas) {
		for(unsigned int i=0; i<datas->size(); i++) {
	//		for(unsigned int j=0; j<datas->at(i).pos->size(); j++) {
				delete datas->at(i).pos;
	//		}
		}
		datas->clear();
	}

	void cleanVectorRegister(vector<Register>* registers) {
		for(unsigned int i=0; i<registers->size(); i++) {
			for(unsigned int j=0; j<registers->at(i).v_data->size(); j++) {
				registers->at(i).v_data->at(j).pos->clear();
				delete registers->at(i).v_data->at(j).pos;
			}
			registers->at(i).v_data->clear();
			delete registers->at(i).v_data;
		}
	}

	void cleanVectorQueryToken(vector<QueryToken>* queryTokens) {
		for(unsigned int i=0; i<queryTokens->size(); i++) {
			cleanQueryToken(queryTokens->at(i));
			queryTokens->at(i).v_data->clear();
			delete queryTokens->at(i).v_data;
		}
	}

	void cleanQueryToken(QueryToken qt) {
		for(unsigned int j=0; j<qt.v_data->size(); j++) {
			qt.v_data->at(j).pos->clear();
			delete qt.v_data->at(j).pos;
		}
	}

	int binarySearch(int posicion, vector<unsigned int>* posiciones) {
		int piso = 0;
		bool encontrado = false;
		int techo = posiciones->size() - 1;
		int medio = (techo + piso) / 2;
		int pos = -1;
		int posicionBuscada;


		while(piso <= techo && !encontrado) {
			posicionBuscada = (posiciones->at(medio));
			if(posicionBuscada > posicion) {
				techo = medio - 1;
			} else if (posicionBuscada < posicion) {
				piso = medio + 1;
			} else {
				encontrado = true;
				pos = medio;
			}

			medio = (techo + piso) / 2;
		}

		return pos;
	}


	int cmp_match(const void* arg1, const void* arg2) {
	//	if(((Match*)arg1)->fraseCompleta && !((Match*)arg2)->fraseCompleta ){
	//		return -1;
	//	}else if(!((Match*)arg1)->fraseCompleta && ((Match*)arg2)->fraseCompleta){
	//		return 1;
	//	}else{
		if (((Match*)arg1)->long_match > ((Match*)arg2)->long_match) return -1;
		if (((Match*)arg2)->long_match > ((Match*)arg1)->long_match) return 1;
	//	}
		return 0;
	}

	string removeSlash(string repo) {
		int lastSlash = repo.find_last_of("/");
		if (lastSlash == repo.size() - 1) //si termina con / se la borro
			repo = repo.substr(0, repo.size() - 1);
		return repo;
	}

	string appendSlash (string repo) {
		int lastSlash = repo.find_last_of("/");
		if (lastSlash != repo.size() - 1) //si no termina con / se la agrego
			repo.append("/");
		return repo;
	}
}
